package com.inspectortime.webapp;

import java.io.Serializable;
import java.text.ParseException;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;

import com.inspectortime.calendar.Day;
import com.inspectortime.calendar.MultiInspectorCalendar;
import com.inspectortime.domain.Inspection;
import com.inspectortime.domain.InspectorCompany;
import com.inspectortime.domain.type.InspectionStatus;
import com.inspectortime.repository.Factory;
import com.inspectortime.repository.Repository;
import com.inspectortime.service.ZipCodeSearchService;
import com.inspectortime.util.CalendarUtil;
import com.inspectortime.webapp.util.HttpSessionUtils;
import com.inspectortime.webapp.util.RequestPathUtils;

public class PickInspectionTimeController extends AbstractController {

	private static Log log = LogFactory
			.getLog(PickInspectionTimeController.class);

	private static int FUTURE_DAYS_TO_SHOW = 21;
	private Repository repository;
	private Factory factory;
	private String formView;

	private ZipCodeSearchService zipCodeSearchService;

	public void setZipCodeSearchService(
			ZipCodeSearchService zipCodeSearchService) {
		this.zipCodeSearchService = zipCodeSearchService;
	}

	public void setFormView(String formView) {
		this.formView = formView;
	}

	public void setSuccessView(String successView) {
		this.successView = successView;
	}

	private String successView;

	protected ModelAndView showPage(HttpServletRequest request,
			HttpServletResponse response) throws Exception {

		Inspection inspection = null;

		// the user may be entering this page by clicking a link in an email
		// if so, load inspection from passed in id and add it to session
		String emailedInspectionId = request.getParameter("id");
		if (StringUtils.hasText(emailedInspectionId)) {
			inspection = repository.loadInspection(emailedInspectionId);
			HttpSessionUtils.storeWizardInspection(request, inspection.getId());
		} else {
			inspection = HttpSessionUtils.retrieveWizardInspection(request,
					repository);
		}

		Calendar startCal = CalendarUtil.createDayCalendar();
		Calendar today = CalendarUtil.createDayCalendar(); // for comparisons
		Calendar tomorrow = CalendarUtil.createDayCalendar(); // for comparisons
		tomorrow.add(Calendar.DATE, 1);

		String startDateStr = request.getParameter("start");

		boolean startParamValid = false;
		if (StringUtils.hasText(startDateStr)) {
			try {
				startCal.setTime(CalendarUtil.formatNavDate(startDateStr));
				startParamValid = true;
			} catch (ParseException e) {
				// Do nothing, use default date
			}
		}

		// Default start-of-week day
		if (!startParamValid) {
			startCal.add(Calendar.DATE, 1);
		}

		String companyShortName = RequestPathUtils
				.extractInspectorCompanyShortNameFromPath(request);

		InspectorCompany company = repository
				.findInspectorCompanyByShortName(companyShortName);
		if (company == null) {
			ModelAndView mv = new ModelAndView("error");
			mv.addObject("message", "Company '" + companyShortName
					+ "' could not be found, check your URL.");
			return mv;
		}
		
		MultiInspectorCalendar consolidatedCalendar = new MultiInspectorCalendar(startCal.getTime(), FUTURE_DAYS_TO_SHOW - 1, company.getInspectors());
		consolidatedCalendar.markScheduledAsUnavailable();
		consolidatedCalendar.disableFarAwayTimeslots(zipCodeSearchService, inspection.getProperty().getLocation().getZipCode());
		Map<String, List<Day>> inspectorDays = consolidatedCalendar.dayListByInspectorId();

		// Generate calendars for navigation
		// If we're viewing a schedule for today's date,
		// don't show a link to view previous dates
		Calendar navCalPrevious = null;
		if (startCal.after(tomorrow)) {
			navCalPrevious = (Calendar) startCal.clone();
			navCalPrevious.add(Calendar.DATE, (-1 * FUTURE_DAYS_TO_SHOW));
		}

		Calendar navCalNext = (Calendar) startCal.clone();
		navCalNext.add(Calendar.DATE, FUTURE_DAYS_TO_SHOW);

		ModelAndView mv = new ModelAndView(formView);
		if (navCalPrevious != null) {
			mv.addObject("navCalPrevious", navCalPrevious.getTime());
		}
		mv.addObject("todayCal", today);
		mv.addObject("navCalNext", navCalNext.getTime());
		mv.addObject("inspectorDays", inspectorDays);
		mv.addObject("inspectors", company.getInspectors());
		mv.addObject("inspectorCompany", company);

		// If user has an inspection in the session (ie is already editing an
		// inspection in the wizard)
		// add it to the view
		mv.addObject("inProgressInspection", inspection);
		return mv;

	}

	private void convertTime(HttpServletRequest request, Inspection inspection) {

		String dateStr = request.getParameter("day");
		String hourStr = request.getParameter("hour");
		String minStr = request.getParameter("minute");
		Calendar inspectionCal = new GregorianCalendar();

		if (inspection.getInspectionTime() == null
				&& !StringUtils.hasText(dateStr)) {
			throw new IllegalArgumentException("Expected 'day' parameter");
		}

		if (StringUtils.hasText(dateStr)) {
			try {
				inspectionCal.setTime(CalendarUtil.formatNavDate(dateStr));
			} catch (ParseException e) {
				throw new IllegalArgumentException("Invalid 'day' parameter: "
						+ dateStr);
			}

			inspectionCal.add(Calendar.HOUR_OF_DAY, Integer.valueOf(hourStr));
			inspectionCal.add(Calendar.MINUTE, Integer.valueOf(minStr));
			inspection.setInspectionTime(inspectionCal.getTime());
		}
	}

	protected ModelAndView submitPage(HttpServletRequest request,
			HttpServletResponse response) throws Exception {

		Inspection inspection = HttpSessionUtils.retrieveWizardInspection(
				request, repository);

		// adding inspector
		String inspectorId = request.getParameter("inspectorId");
		inspection.setInspector(repository.loadInspector(inspectorId));

		// changing time?
		convertTime(request, inspection); // add time params

		// Set status
		if (inspection.getInspectionStatus() == InspectionStatus.INCOMPLETE) {
			inspection.setInspectionStatus(InspectionStatus.QUICK_BOOK);
		}

		// Calculate and save fee
		String companyShortName = RequestPathUtils
				.extractInspectorCompanyShortNameFromPath(request);
		InspectorCompany inspectorCompany = repository
				.findInspectorCompanyByShortName(companyShortName);
		inspection.setInspectionFee(inspectorCompany
				.calculateInspectionFee(inspection));

		Serializable inspectionId = inspection.save();
		HttpSessionUtils.storeWizardInspection(request, inspectionId);

		return new ModelAndView(successView);
	}

	@Override
	protected ModelAndView handleRequestInternal(HttpServletRequest request,
			HttpServletResponse response) throws Exception {

		String inspectorId = request.getParameter("inspectorId");
		if (StringUtils.hasText(inspectorId)) {
			return submitPage(request, response);
		}
		return showPage(request, response);

	}

	public void setRepository(Repository repository) {
		this.repository = repository;
	}

	public void setFactory(Factory factory) {
		this.factory = factory;
	}

}
